package com.xplay.xpocker.config.security;
import com.xplay.xpocker.entity.system.VerificationMode;
import com.xplay.xpocker.util.BusinessAssertion;
import com.xplay.xpocker.util.DictionaryConst;
import com.xplay.xpocker.util.RedisUtil;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Coordinate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;

/**
 * @author wanjie
 * @date 2021/4/20 20:15
 */
@Component
@PropertySource(value = "classpath:filepath.properties")
public class VerificationUtil {
    @Value("${windows.download.file.path.verification}")
    private String winVerificationImagePath;

    @Value("${linux.download.file.path.verification}")
    private String linuxVerificationImagePath;
    @Autowired
    private RedisUtil redisUtil;

    private Logger logger = LoggerFactory.getLogger(VerificationUtil.class);


    // 控制滑动验证码  的误差
    private final double CODE_MOVE_GAY = 0.05f;


    public boolean checkGenerateCode(VerificationMode mode, boolean delete) {
        if (mode == null) {
            return false;
        }
        BusinessAssertion.isNull("请输入验证码", mode, mode.getX(), mode.getY());
        String codeKey = DictionaryConst.UserRedisHeader.VERIFICATION_CODE + mode.getId();
        VerificationMode menMode = (VerificationMode) redisUtil.get(codeKey);
        BusinessAssertion.isNull("验证码不存在", menMode);
        logger.info("验证码误差x=[{}],y=[{}]", Math.abs(mode.getX() - menMode.getX()), Math.abs(mode.getY() - menMode.getY()));
        if (Math.abs(mode.getX() - menMode.getX()) <= CODE_MOVE_GAY && Math.abs(mode.getY() - menMode.getY()) <= CODE_MOVE_GAY) {
            if (delete) {
                redisUtil.del(codeKey);
            }
            return true;
        }
        return false;
    }


    /**
     * 获取路径下的所有文件/文件夹
     *
     * @param directoryPath  需要遍历的文件夹路径
     * @param isAddDirectory 是否将子文件夹的路径也添加到list集合中
     * @return
     */
    public List<String> getAllFile(String directoryPath, boolean isAddDirectory) {
        List<String> list = new ArrayList<String>();
        File baseFile = new File(directoryPath);
        if (baseFile.isFile() || !baseFile.exists()) {
            return list;
        }
        File[] files = baseFile.listFiles();
        for (File file : files) {
            if (file.isFile()) {
                list.add(file.getAbsolutePath());
            } else if (file.isDirectory() && isAddDirectory) {
                list.addAll(getAllFile(file.getAbsolutePath(), isAddDirectory));
            }
        }
        return list;
    }

    public VerificationMode generateVli() {
        VerificationMode verificationMode = new VerificationMode();
        try {
            String rootPath = linuxVerificationImagePath;
            String os = System.getProperty("os.name");
            if (os.toLowerCase().startsWith("win")) {
                rootPath = winVerificationImagePath;
            }
            List<String> allFile = getAllFile(rootPath, false);
            int randomInt = new Random().nextInt(allFile.size());
            File file = new File(allFile.get(randomInt));
            Thumbnails.Builder<File> builder = Thumbnails.of(file);
            BufferedImage bim = ImageIO.read(file);
            int imgWidth = bim.getWidth();
            int imgHeight = bim.getHeight();
            verificationMode.setScale(new BigDecimal((float) imgWidth / imgHeight).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            int cropHeight = (int) (imgHeight * 0.2);
            int maxRandomWidth = imgWidth - cropHeight;
            int maxRandomHeight = imgHeight - cropHeight;
            int randomHeight = new Random().nextInt(maxRandomHeight);
            int randomWidth = new Random().nextInt(maxRandomWidth);
            verificationMode.setId(UUID.randomUUID().toString().replace("-", ""));
            verificationMode.setX(new BigDecimal((float) randomWidth / imgWidth).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            verificationMode.setY(new BigDecimal((float) randomHeight / imgHeight).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            // 裁剪图片
            builder.sourceRegion(randomWidth, randomHeight, cropHeight, cropHeight).size(cropHeight, cropHeight);
            verificationMode.setMoveImage(getBase64ByBufferedImage(builder.asBufferedImage()));
            // 将图片变为灰色
            BufferedImage grayImage = getGrayImage(builder.asBufferedImage());
            verificationMode.setBackGroundImage(getBase64ByBufferedImage(Thumbnails.of(allFile.get(randomInt)).size(imgWidth, imgHeight).watermark(new Coordinate(randomWidth, randomHeight), grayImage, 1f)
                    .outputQuality(0.8f).asBufferedImage()));
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
        return verificationMode;
    }

    public String getBase64ByBufferedImage(BufferedImage image) {
        try {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            ImageIO.write(image, "png", stream);
            byte[] bytes = stream.toByteArray();//转换成字节
            BASE64Encoder encoder = new BASE64Encoder();
            String png_base64 = encoder.encodeBuffer(bytes).trim().replaceAll("\n", "").replaceAll("\r", "");//转换成base64串
            // png_base64 = "data:image/jpg;base64," + png_base64.replaceAll("\n", "").replaceAll("\r", "");
            return png_base64;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 将图片置灰 ---  使用现成的类
     *
     * @param originalImage
     * @return
     */
    public BufferedImage getGrayImage(BufferedImage originalImage) {
        BufferedImage grayImage;
        int imageWidth = originalImage.getWidth();
        int imageHeight = originalImage.getHeight();
        //TYPE_3BYTE_BGR -->  表示一个具有 8 位 RGB 颜色分量的图像，对应于 Windows 风格的 BGR 颜色模型，具有用 3 字节存储的 Blue、Green 和 Red 三种颜色。
        grayImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR);
        ColorConvertOp cco = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
        cco.filter(originalImage, grayImage);
        return grayImage;
    }

    /**
     * 将图片置灰 --- 手动
     *
     * @param originalImage
     * @return
     */
    public BufferedImage getGrayImageManual(BufferedImage originalImage) {
        int green = 0, red = 0, blue = 0, rgb;
        int imageWidth = originalImage.getWidth();
        int imageHight = originalImage.getHeight();
        BufferedImage routeImage = new BufferedImage(imageWidth, imageHight, BufferedImage.TYPE_3BYTE_BGR);
        for (int i = originalImage.getMinX(); i < imageWidth; i++) {
            for (int j = originalImage.getMinY(); j < imageHight; j++) {
                //获取该点像素，用Object类型标识
                Object data = routeImage.getRaster().getDataElements(i, j, null);
                red = routeImage.getColorModel().getRed(data);
                green = routeImage.getColorModel().getGreen(data);
                blue = routeImage.getColorModel().getBlue(data);
                red = (red * 3 + green * 6 + blue * 1) / 10;
                green = red;
                blue = green;
                rgb = (red * 256 + green) * 256 + blue;
                if (rgb > 8388608) {
                    rgb = rgb - 256 * 256 * 256;
                }
                //将rgb值写回图片
                routeImage.setRGB(i, j, rgb);
            }
        }
        return routeImage;
    }
}
